{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Supervised Learning (Classification)\n",
    "\n",
    "In supervised learning, the task is to infer hidden structure from\n",
    "labeled data, comprised of training examples $\\{(x_n, y_n)\\}$.\n",
    "Classification means the output $y$ takes discrete values.\n",
    "\n",
    "We demonstrate with an example in Edward. A webpage version is available at\n",
    "http://edwardlib.org/tutorials/supervised-classification."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from __future__ import absolute_import\n",
    "from __future__ import division\n",
    "from __future__ import print_function\n",
    "\n",
    "import edward as ed\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "\n",
    "from edward.models import Bernoulli, MultivariateNormalTriL, Normal\n",
    "from edward.util import rbf\n",
    "from observations import crabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data\n",
    "\n",
    "Use the\n",
    "[crabs data set](https://stat.ethz.ch/R-manual/R-devel/library/MASS/html/crabs.html),\n",
    "which consists of morphological measurements on a crab species. We\n",
    "are interested in predicting whether a given crab has the color form\n",
    "blue (encoded as 0) or orange (encoded as 1). We use all the numeric features\n",
    "in the dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of data points: 100\n",
      "Number of features: 5\n"
     ]
    }
   ],
   "source": [
    "ed.set_seed(42)\n",
    "\n",
    "data, metadata = crabs(\"~/data\")\n",
    "X_train = data[:100, 3:]\n",
    "y_train = data[:100, 1]\n",
    "\n",
    "N = X_train.shape[0]  # number of data points\n",
    "D = X_train.shape[1]  # number of features\n",
    "\n",
    "print(\"Number of data points: {}\".format(N))\n",
    "print(\"Number of features: {}\".format(D))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model\n",
    "\n",
    "A Gaussian process is a powerful object for modeling nonlinear\n",
    "relationships between pairs of random variables. It defines a distribution over\n",
    "(possibly nonlinear) functions, which can be applied for representing\n",
    "our uncertainty around the true functional relationship.\n",
    "Here we define a Gaussian process model for classification\n",
    "(Rasumussen & Williams, 2006).\n",
    "\n",
    "Formally, a distribution over functions $f:\\mathbb{R}^D\\to\\mathbb{R}$ can be specified\n",
    "by a Gaussian process\n",
    "$$\n",
    "\\begin{align*}\n",
    "  p(f)\n",
    "  &=\n",
    "  \\mathcal{GP}(f\\mid \\mathbf{0}, k(\\mathbf{x}, \\mathbf{x}^\\prime)),\n",
    "\\end{align*}\n",
    "$$\n",
    "whose mean function is the zero function, and whose covariance\n",
    "function is some kernel which describes dependence between\n",
    "any set of inputs to the function.\n",
    "\n",
    "Given a set of input-output pairs\n",
    "$\\{\\mathbf{x}_n\\in\\mathbb{R}^D,y_n\\in\\mathbb{R}\\}$,\n",
    "the likelihood can be written as a multivariate normal\n",
    "\n",
    "\\begin{align*}\n",
    "  p(\\mathbf{y})\n",
    "  &=\n",
    "  \\text{Normal}(\\mathbf{y} \\mid \\mathbf{0}, \\mathbf{K})\n",
    "\\end{align*}\n",
    "\n",
    "where $\\mathbf{K}$ is a covariance matrix given by evaluating\n",
    "$k(\\mathbf{x}_n, \\mathbf{x}_m)$ for each pair of inputs in the data\n",
    "set.\n",
    "\n",
    "The above applies directly for regression where $\\mathbb{y}$ is a\n",
    "real-valued response, but not for (binary) classification, where $\\mathbb{y}$\n",
    "is a label in $\\{0,1\\}$. To deal with classification, we interpret the\n",
    "response as latent variables which is squashed into $[0,1]$. We then\n",
    "draw from a Bernoulli to determine the label, with probability given\n",
    "by the squashed value.\n",
    "\n",
    "Define the likelihood of an observation $(\\mathbf{x}_n, y_n)$ as\n",
    "\n",
    "\\begin{align*}\n",
    "  p(y_n \\mid \\mathbf{z}, x_n)\n",
    "  &=\n",
    "  \\text{Bernoulli}(y_n \\mid \\text{logit}^{-1}(\\mathbf{x}_n^\\top \\mathbf{z})).\n",
    "\\end{align*}\n",
    "\n",
    "Define the prior to be a multivariate normal\n",
    "\n",
    "\\begin{align*}\n",
    "  p(\\mathbf{z})\n",
    "  &=\n",
    "  \\text{Normal}(\\mathbf{z} \\mid \\mathbf{0}, \\mathbf{K}),\n",
    "\\end{align*}\n",
    "\n",
    "with covariance matrix given as previously stated.\n",
    "\n",
    "Let's build the model in Edward. We use a radial basis function (RBF)\n",
    "kernel, also known as the squared exponential or exponentiated\n",
    "quadratic. It returns the kernel matrix evaluated over all pairs of\n",
    "data points; we then Cholesky decompose the matrix to parameterize the\n",
    "multivariate normal distribution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "X = tf.placeholder(tf.float32, [N, D])\n",
    "f = MultivariateNormalTriL(loc=tf.zeros(N), scale_tril=tf.cholesky(rbf(X)))\n",
    "y = Bernoulli(logits=f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, we define a placeholder `X`. During inference, we pass in\n",
    "the value for this placeholder according to data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inference\n",
    "\n",
    "Perform variational inference.\n",
    "Define the variational model to be a fully factorized normal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "qf = Normal(loc=tf.get_variable(\"qf/loc\", [N]),\n",
    "            scale=tf.nn.softplus(tf.get_variable(\"qf/scale\", [N])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run variational inference for `5000` iterations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5000/5000 [100%] ██████████████████████████████ Elapsed: 9s | Loss: 78.369\n"
     ]
    }
   ],
   "source": [
    "inference = ed.KLqp({f: qf}, data={X: X_train, y: y_train})\n",
    "inference.run(n_iter=5000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this case\n",
    "`KLqp` defaults to minimizing the\n",
    "$\\text{KL}(q\\|p)$ divergence measure using the reparameterization\n",
    "gradient.\n",
    "For more details on inference, see the [$\\text{KL}(q\\|p)$ tutorial](/tutorials/klqp).\n",
    "(This example happens to be slow because evaluating and inverting full\n",
    "covariances in Gaussian processes happens to be slow.)"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
